package at.newmedialab.test.base

import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.RESTClient
import org.junit.Assert

/**
 * The WebserviceBaseTest allows to process request-response specifications represented as Groovy Map structures of the
 * following form:
 * spec = [
 *      'method'  : 'GET' / 'PUT' / 'POST' / 'DELETE',
 *      'path'    : '/PATH/TO/WEBSERVICE',
 *      'request' : [
 *              'headers' : [ key1 : value1, key2 : value2 ],
 *              'query'   : [ param1 : value1, param2 : value2 ],
 *              'contentType' : MIME CONTENT TYPE
 *              'body'    : VALUE FOR BODY
 *      ],
 *      'response' : [
 *              'code'    : 200 (or other integer value),
 *              'headers' : [ key1 : value1, key2 : value2 ],
 *              'body'    : VALUE FOR BODY
 *      ]
 * ]
 *
 * The keys 'method', 'path', 'request', and 'response' are mandatory:
 * - 'method' represents the HTTP method to use ('GET', 'PUT', 'POST', 'DELETE' )
 * - 'path' is the relative path (location) of the webservice to call
 * - 'request' is a map specifying the request to send and contains the following optional key / value pairs:
 *    - 'headers' is a map of key/value pairs for HTTP headers to send
 *    - 'query' is a map of key/value pairs of HTTP URL query parameters to add to the URL using ?key=value
 *    - 'body' is a string representing a body to be sent as part of the request
 *    - 'contentType' is a MIME type describing the type of the content sent as body; defaults to ANY
 *
 * - 'response' is a map specifying how the expected response looks like; it may contain the following optional key/value pairs:
 *    - 'code' is an integer representing the expected HTTP status code
 *    - 'headers' is a map from header name to string or regular expression specifying expected HTTP headers and their contents
 *    - 'body' is a string or regular expression representing the expected return body of the request
 *
 *
 *
 * User: sschaffe
 */
class WebserviceBaseTest {

    private boolean initialised = false;

    protected String baseUrl;

    /**
     * Initialise the webservice configuration; reads the KiWi configuration file to determine the host name
     */
    protected void init() {

        if(!initialised) {
            def props = new Properties()

            getClass().getClassLoader().getResourceAsStream("default-config.properties").withStream {
                stream -> props.load(stream)
            }

            println "KiWi Configuration loaded (version: " + props['kiwi.version'] + ")"

            baseUrl = props["kiwi.host"]

            println "Webservice Base: " + baseUrl

            initialised = true;
        }
    }


    protected Object GET(String serviceUrl, Map<String,String> parameters, Map<String,String> headers, String contentType, String body) {
        init();

        RESTClient client = new RESTClient(baseUrl + serviceUrl)
        result = client.get([
                'query'       : parameters,
                'headers'     : headers,
                'contentType' : contentType,
                'body'        : body
        ])
        return result;
    }


    protected void process(Map<String,?> spec) {
        init();

        def serviceUrl = spec.path

        def requestMap = [:]

        if(spec.request?.query != null) {
            requestMap.query   = spec.request?.query
        }

        if(spec.request?.headers != null) {
            requestMap.headers = spec.request?.headers
        } else {
            requestMap.headers = [:]
        }

        if(spec.request?.body != null) {
            requestMap.body    = spec.request?.body
        }

        if(spec.request?.contentType != null) {
            requestMap.requestContentType = spec.request?.contentType
        } else if(spec.request?.headers?.containsKey('Content-Type')) {
            requestMap.requestContentType = spec.request?.headers['Content-Type']
        } else {
            requestMap.requestContentType = ContentType.JSON;
        }
        requestMap.headers?.'Content-Type' = requestMap.requestContentType


        if(requestMap.headers.'Accept' == null) {
            requestMap.headers.'Accept' = 'application/json'
        }


        println requestMap


        def client = new HTTPBuilder()


        def method = null;
        def result = null;

        switch (spec.method) {
            case "GET" :
                method = Method.GET
                break
            case "PUT" :
                method = Method.PUT
                break
            case "POST" :
                method = Method.POST
                break
            case "DELETE" :
                method = Method.DELETE
                break
            default :
                Assert.fail("invalid request type: " + spec.method)
        }

        result = client.request (baseUrl + serviceUrl, method, ContentType.TEXT) { req ->
            uri.query = requestMap.query
            headers   = requestMap.headers

            if(method != Method.GET && method != Method.DELETE && requestMap.body != null) {
                body      = requestMap.body
            }

            response.success = { resp, reader ->
                println "Request " + method + " " + uri  + " successful ("+ resp.statusLine.statusCode +")"
                if(spec.response?.code != null) {
                    Assert.assertEquals(spec.response.code, resp.statusLine.statusCode)
                }
                if(spec.response?.body != null) {
                    def retval = reader.text
                    Assert.assertTrue("Return value was "+retval, retval ==~ spec.response.body)
                }
            }

            response.failure = { resp, reader ->
                println "Request " + method + " " + uri  + " failed ("+ resp.statusLine.statusCode +")"
                if(spec.response?.code != null) {
                    Assert.assertEquals(spec.response.code, resp.statusLine.statusCode)
                }
                if(spec.response?.body != null) {
                    def retval = reader.text
                    Assert.assertTrue ("Return value was "+retval, retval ==~ spec.response.body)
                }
            }
        }


    }

}
